﻿using System;
using System.Runtime.InteropServices;
using System.Threading;
using SDL2;
namespace audiotest
{
    class Program
    {
        unsafe static void Main(string[] args)
        {

            Console.WriteLine("Hello World!");

            //audio test
            var result = SDL.SDL_Init(SDL.SDL_INIT_AUDIO | SDL.SDL_INIT_TIMER);
            Console.WriteLine("init sdl audio=" + result);

            SDL.SDL_AudioSpec wanted_spec = new SDL.SDL_AudioSpec();
            wanted_spec.freq = 48000;
            wanted_spec.format = SDL.AUDIO_S16;
            wanted_spec.channels = 1;
            wanted_spec.silence = 0;
            wanted_spec.samples = 320;
            wanted_spec.callback = SDL_AudioCallback;
            try
            {
                var resultopen = SDL.SDL_OpenAudio(ref wanted_spec, IntPtr.Zero);
                Console.WriteLine("open sdl audio=" + resultopen);
                if (resultopen < 0)
                {
                    var strerr = SDL.SDL_GetError();
                    Console.WriteLine("open sdl audio=" + strerr);

                }
                SDL.SDL_PauseAudio(0);
            }
            catch (Exception err)
            {
                return;
            }

            var console = new Nescafe.Console();
            console.LoadCartridge("d:\\3.nes");
            var consoleThread = new Thread(() => console.Start());
            consoleThread.IsBackground = true;
            consoleThread.Start();

            console.Apu.writeSample += writeSample;
            System.Threading.Thread.Sleep(1000);
            //console.Controller.setButtonState(Nescafe.Controller.Button.Start, true);
            System.Threading.Thread.Sleep(100);
            //console.Controller.setButtonState(Nescafe.Controller.Button.Start, false);


            var win = SDL.SDL_CreateWindow("hi window", 100, 100, 500, 300, SDL.SDL_WindowFlags.SDL_WINDOW_SHOWN | SDL.SDL_WindowFlags.SDL_WINDOW_RESIZABLE);

            var tagetsurface = SDL.SDL_GetWindowSurface(win);
            SDL.SDL_Surface tt = Marshal.PtrToStructure<SDL.SDL_Surface>(tagetsurface);
            var ttformat = Marshal.PtrToStructure<SDL.SDL_PixelFormat>(tt.format);

            var targetmid = SDL.SDL_CreateRGBSurfaceWithFormat(tt.flags, 256, 240, ttformat.BitsPerPixel, ttformat.format);
            var bmp1 = SDL.SDL_LoadBMP("d:\\1.bmp");
            SDL.SDL_Rect src = new SDL.SDL_Rect() { x = 0, y = 0, w = 256, h = 240 };
            SDL.SDL_Surface surface = Marshal.PtrToStructure<SDL.SDL_Surface>(bmp1);

            SDL.SDL_BlitSurface(bmp1, ref src, targetmid, ref src);

            SDL.SDL_GetWindowSize(win, out int ww, out int wh);
            SDL.SDL_Rect windest = new SDL.SDL_Rect() { x = 0, y = 0, w = ww, h = wh };
            SDL.SDL_SoftStretch(targetmid, ref src, tagetsurface, ref windest);
            SDL.SDL_UpdateWindowSurface(win);
            console.DrawAction += (byte[] datas) =>
              {
                  var time1 = DateTime.Now;
                  SDL.SDL_LockSurface(bmp1);
                  {
                      for (var line = 0; line < 240; line++)
                      {
                          byte* linestart = (byte*)surface.pixels + surface.pitch * line;
                          for (var x = 0; x < 256; x++)
                          {
                              var c = datas[line * 256 + x];
                              linestart[x * 3] = c;
                              linestart[x * 3 + 1] = c;
                              linestart[x * 3 + 2] = c;
                          }
                      }
                  }
                  SDL.SDL_UnlockSurface(bmp1);
                  var time2 = DateTime.Now;
                  var timelast = (time2 - time1).TotalMilliseconds;

              };

            SDL.SDL_SetWindowBordered(win, SDL.SDL_bool.SDL_TRUE);
            while (true)
            {
                int dd = SDL.SDL_PollEvent(out SDL.SDL_Event _event);
                if(dd>0)
                {
                    if (_event.type == SDL.SDL_EventType.SDL_QUIT)
                    {
                        return;
                    }
                    if (_event.type == SDL.SDL_EventType.SDL_WINDOWEVENT)
                    {
                        if (_event.window.windowEvent == SDL.SDL_WindowEventID.SDL_WINDOWEVENT_CLOSE)
                        {
                            return;
                        }
                        if (_event.window.windowEvent == SDL.SDL_WindowEventID.SDL_WINDOWEVENT_SIZE_CHANGED)
                        {
                            SDL.SDL_BlitSurface(bmp1, ref src, targetmid, ref src);

                            SDL.SDL_GetWindowSize(win, out windest.w, out windest.h);

                            var _tagetsurface = SDL.SDL_GetWindowSurface(win);
                            SDL.SDL_SoftStretch(targetmid, ref src, _tagetsurface, ref windest);
                            SDL.SDL_UpdateWindowSurface(win);

                        }
                    }
                    continue;
                }
                SDL.SDL_BlitSurface(bmp1, ref src, targetmid, ref src);
                var _tagetsurface2 = SDL.SDL_GetWindowSurface(win);
                SDL.SDL_SoftStretch(targetmid, ref src, _tagetsurface2, ref windest);
                SDL.SDL_UpdateWindowSurface(win);
                System.Threading.Thread.Sleep(1);
                continue;

            }
        }
        static byte[] bs = new byte[1600];
        static System.Collections.Concurrent.ConcurrentQueue<byte[]> audioframe = new System.Collections.Concurrent.ConcurrentQueue<byte[]>();
        static int pos;
        static void writeSample(float sample, int i)
        {
            var b =(Int16)( sample * 0xffff);
            var bb = BitConverter.GetBytes(b);
            bs[pos] = bb[0];
            bs[pos + 1] = bb[1];
            pos+=2;
            if (pos == 1600)
            {
                pos = 0;
                audioframe.Enqueue(bs);
                bs = new byte[1600];
            }
        }
        static Random ran = new Random();
        static unsafe void SDL_AudioCallback(IntPtr userdata, IntPtr stream, int len)
        {
            if (audioframe.Count > 0)
            {
                audioframe.TryPeek(out byte[] r);
                ran.NextBytes(r);
                byte* data = (byte*)stream;
                for (var i = 0; i < r.Length; i++)
                {
                    data[i] = r[i];
                }
            }
            //Console.WriteLine("??");
        }

    }
}
